package bblvolume

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/facebookgo/httpcontrol"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"
)

const (
	KiB           = 1024
	MiB           = 1024 * KiB
	GiB           = 1024 * MiB
	PUT_FILE_SIZE = 4 * MiB
)

//var http_client http.Client

var http_client = &http.Client{
	Transport: &httpcontrol.Transport{
		RequestTimeout: 0 * time.Second,
	},
}

func Join(addr, uuid, body string) error {
	req, err := http.NewRequest("JOIN", fmt.Sprintf("http://%v/%v", addr, uuid), strings.NewReader(body))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err := http_client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	log.Debug("JOIN\t", req, "\t\n", resp)
	if resp.StatusCode == 200 {
		return nil
	}
	return fmt.Errorf("%v %v", resp.StatusCode, resp.Status)
}

func Leave(addr, uuid string) error {
	req, err := http.NewRequest("LEAVE", fmt.Sprintf("http://%v/%v", addr, uuid), nil)
	if err != nil {
		return err
	}
	resp, err := http_client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	log.Debug("LEAVE\t", req, "\t\n", resp)
	if resp.StatusCode == 200 {
		return nil
	}
	return fmt.Errorf("%v %v", resp.StatusCode, resp.Status)
}

func GetJob(url string, vol *VolStat) ([]Job, error) {
	b, _ := json.Marshal(*vol)
	req, err := http.NewRequest("GET_JOB", url, bytes.NewReader(b))
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err := http_client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	job := []Job{}
	if resp.StatusCode == 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}
		return job, json.Unmarshal(data, &job)
	}
	return job, fmt.Errorf("error %v", resp.Status)
}

func PostFile(file, url string) (writen int64, err error) {
	f, err := os.OpenFile(file, os.O_RDONLY, os.ModePerm)
	if err != nil {
		return 0, err
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		return 0, err
	}
	if fi.Size() <= PUT_FILE_SIZE {
		req, err := http.NewRequest("PUT", url, f)
		if err != nil {
			return 0, err
		}
		req.Header.Add("Content-Type", "application/octet-stream")
		req.ContentLength = fi.Size()
		resp, err := http_client.Do(req)
		if err != nil {
			return 0, err
		}
		log.Debug("\t", req, "\t\n", resp)
	} else {
		body_buf := bytes.NewBufferString("")
		body_writer := multipart.NewWriter(body_buf)
		// use the body_writer to write the Part headers to the buffer
		_, err := body_writer.CreateFormFile("object", f.Name())
		if err != nil {
			return 0, err
		}
		// need to know the boundary to properly close the part myself.
		boundary := body_writer.Boundary()
		close_string := fmt.Sprintf("\r\n--%s--\r\n", boundary)
		close_buf := bytes.NewBufferString(close_string)
		// use multi-reader to defer the reading of the file data until writing to the socket buffer.
		request_reader := io.MultiReader(body_buf, f, close_buf)
		req, err := http.NewRequest("POST", url, request_reader)
		if err != nil {
			return 0, err
		}

		// Set headers for multipart, and Content Length
		req.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary)
		req.ContentLength = fi.Size() + int64(body_buf.Len()) + int64(close_buf.Len())

		resp, err := http_client.Do(req)
		if err != nil {
			return 0, err
		}
		defer resp.Body.Close()
		log.Debug("POST \t", req, "\t\n", resp)
	}
	return fi.Size(), nil
}
